﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dnAnalytics;
using dnAnalytics.Math;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Drawing;

namespace CalcSharp.Core.Containers
{
    [Serializable]
    public class Int24Matrix : AMatrix
    {
        private readonly Int24[] mData;
        internal Int24[] Data { get { return mData; } }

        #region Constructors
        public Int24Matrix(int rows, int columns): base(rows, columns)
        {
            mData = new Int24[rows * columns];
            for (int i = 0; i < mData.Length; i++) mData[i] = new Int24();
        }
        public Int24Matrix(int rows, int columns, Int24 value): this(rows, columns)
        {
            for (int i = 0; i < mData.Length; i++)
            {
                mData[i] = value;
            }
        }
        public Int24Matrix(Int24Matrix other): this(other.Rows, other.Columns)
        {
            if (other != null)
            {
                for (int i = 0; i < other.mData.Length; i++) this.mData[i] = other.mData[i];
                //Buffer.BlockCopy(other.mData, 0, mData, 0, mData.Length * sizeof(float));
            }
        }
        public Int24Matrix(Int24[,] array): this(array.GetLength(0), array.GetLength(1))
        {
            for (int j = 0; j < Columns; j++)
            {
                int jIndex = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    mData[jIndex + i] = array[i, j];
                }
            }
        }
        #endregion

        public override void CopyTo(AMatrix target)
        {
            if (target == null) throw new ArgumentNullException("target");
            if (Rows != target.Rows || Columns != target.Columns) throw new Exception("Target and Source row/column count mismatch");

            Int24Matrix Dbl = target as Int24Matrix;
            if (Dbl == null) base.CopyTo(target);
            else
            {
                for (int i = 0; i < target.Rows; i++)
                {
                    for (int j = 0; j < target.Columns; j++)
                    {
                        target[i, j] = this.ValueAt(i, j);
                    }
                }
            }
        }

        public override AMatrix Clone()
        {
            return new Int24Matrix(this);
        }

        #region Abstract implementations
        protected internal override void ValueAt(int row, int column, double value)
        {
            Int24 val = new Int24();
            if (value > Int24.MaxValue) val.FromInt32(Int24.MaxValue);
            else if (value < Int24.MinValue) val.FromInt32(0);
            else val.FromInt32((int)value);
            mData[column * Rows + row] = val;
        }

        protected internal override double ValueAt(int row, int column)
        {
            return mData[column * Rows + row].ToInt32();
        }

        protected internal override AMatrix CreateMatrix(int numberOfRows, int numberOfColumns)
        {
            return new Int24Matrix(numberOfRows, numberOfColumns);
        }

        public override double Determinant()
        {
            throw new CSException("Function not supported by this storage engine");
        }

        public override AMatrix Transpose()
        {
            Int24Matrix ret = new Int24Matrix(Columns, Rows);
            for (int j = 0; j < Columns; j++)
            {
                int index = j * Rows;
                for (int i = 0; i < Rows; i++)
                {
                    ret.mData[i * Columns + j] = mData[index + i];
                }
            }
            return ret;
        }

        public override AMatrix Inverse()
        {
            throw new CSException("Function not supported by this storage engine");
        }

        public override void Clear()
        {
            Array.Clear(mData, 0, mData.Length);
        }

        public override double[] GetRow(int index)
        {
            double[] array = new double[this.Columns];
            for (int i = 0; i < this.Columns; i++) array[i] = ValueAt(index, i);
            return array;
        }

        public override double[] GetColumn(int index)
        {
            double[] array = new double[this.Rows];
            for (int i = 0; i < this.Rows; i++) array[i] = ValueAt(i, index);
            return array;
        }

        public override void SetColumn(int index, double[] source)
        {
            if (index < 0 || index >= Columns) throw new ArgumentOutOfRangeException("index");
            if (source == null) throw new ArgumentNullException("source");
            if (source.Length != Rows) throw new Exception("Array element number is not equal with matrix row count");
            for (int i = 0; i < Rows; i++) ValueAt(i, index, source[i]);
        }

        public override void SetRow(int index, double[] source)
        {
            if (index < 0 || index >= Rows) throw new ArgumentOutOfRangeException("index");
            if (source == null) throw new ArgumentNullException("source");
            if (source.Length != Columns) throw new Exception("Array element number is not equal with matrix Column count");
            for (int j = 0; j < Columns; j++) ValueAt(index, j, source[j]);
        }

        public override Bitmap GetPreview()
        {
            Bitmap ret;
            int i, j;
            int ncols, nrows;
            Int24 pixval = new Int24();
            if (this.Rows * this.Columns > 600 * 600)
            {
                double zoom = Math.Min((double)this.Columns / 600, (double)this.Rows / 600);
                ncols = (int)(zoom * this.Columns);
                nrows = (int)(zoom * this.Rows);
                ret = new Bitmap(ncols, nrows);
                double xfact = (double)this.Columns / ncols;
                double yfact = (double)this.Rows / nrows;

                for (i = 0; i < nrows; i++)
                {
                    for (j = 0; j < ncols; j++)
                    {
                        pixval = (int)this[(int)(Math.Floor(i * yfact)), (int)(Math.Floor(j * xfact))];
                        ret.SetPixel(j, i, pixval.ToColor());
                    }
                }
                return ret;
            }
            else
            {
                ret = new Bitmap(this.Columns, this.Rows);
                for (i = 0; i < this.Rows; i++)
                {
                    for (j = 0; j < this.Columns; j++)
                    {
                        pixval = (int)this[i, j];
                        ret.SetPixel(j, i, pixval.ToColor());
                    }
                }
                return ret;
            }
        }

        #endregion

        #region Features

        public static Int24Matrix operator +(Int24Matrix left, Int24Matrix right)
        {
            if (left == null) throw new ArgumentNullException("left");
            if (right == null) throw new ArgumentNullException("right");
            if (left.Rows != right.Rows || left.Columns != right.Columns) throw new Exception("Input matrix row/column count mismatch");
            Int24Matrix ret = new Int24Matrix(left.Rows, left.Columns);
            for (int i = 0; i < left.Rows; i++)
            {
                for (int j = 0; j < right.Columns; j++)
                {
                    ret[i, j] = left[i, j] + right[i, j];
                }
            }
            return ret;
        }

        public static Int24Matrix operator -(Int24Matrix left, Int24Matrix right)
        {
            if (left == null) throw new ArgumentNullException("left");
            if (right == null) throw new ArgumentNullException("right");
            if (left.Rows != right.Rows || left.Columns != right.Columns) throw new Exception("Input matrix row/column count mismatch");
            Int24Matrix ret = new Int24Matrix(left.Rows, left.Columns);
            for (int i = 0; i < left.Rows; i++)
            {
                for (int j = 0; j < right.Columns; j++)
                {
                    ret[i, j] = left[i, j] - right[i, j];
                }
            }
            return ret;
        }

        public static Int24Matrix operator *(Int24Matrix left, Int24Matrix right)
        {
            if (left == null) throw new ArgumentNullException("left");
            if (right == null) throw new ArgumentNullException("right");
            if (left.Columns != right.Rows) throw new Exception("Input matrix row/column count mismatch");
            Int24Matrix ret = new Int24Matrix(left.Rows, right.Columns);

            for (int j = 0; j != right.Columns; j++)
            {
                for (int i = 0; i != left.Rows; i++)
                {
                    double s = 0;
                    for (int l = 0; l != left.Columns; l++)
                    {
                        s += left[i, l] * right[l, j];
                    }
                    ret[i, j] = s;
                }
            }
            return ret;
        }

        public static Int24Matrix operator /(Int24Matrix left, Int24Matrix right)
        {
            if (left == null) throw new ArgumentNullException("left");
            if (right == null) throw new ArgumentNullException("right");
            if (left.Rows != right.Rows && left.Columns != right.Columns) throw new Exception("Input matrix row/column count mismatch");
            Int24Matrix ret = new Int24Matrix(left.Rows, left.Columns);

            for (int i = 0; i < ret.Rows; i++)
            {
                for (int j = 0; j < ret.Columns; j++)
                {
                    ret[i, j] = left[i, j] / right[i, j];
                }
            }
            return ret;
        }

        #endregion
    }
}